package org.idea.spring.aop.version2.config;

import org.idea.spring.aop.version1.aspect.MyAspect;
import org.idea.spring.aop.version2.annotation.IAfter;
import org.idea.spring.aop.version2.annotation.IAspect;
import org.idea.spring.aop.version2.annotation.IBefore;
import org.idea.spring.aop.version2.annotation.IPointcut;
import org.idea.spring.aop.version2.bean.Advisor;
import org.idea.spring.aop.version2.bean.AspectJPointcut;
import org.idea.spring.aop.version2.bean.Pointcut;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author linhao
 * @Date created in 3:50 下午 2021/5/14
 */
@Component
public class AnnotationParser implements ConfigParser, ApplicationContextAware, BeanFactoryAware, InitializingBean {

    private ApplicationContext applicationContext;

    private BeanFactory beanFactory;

    private final Map<String, Advisor> cache = new ConcurrentHashMap<>();

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public List<Advisor> parse() {
        if (applicationContext == null || beanFactory == null) {
            throw new RuntimeException("applicationContext is null");
        }
        List<Advisor> advisorList = new ArrayList<>();
        //获取所有的aspect对象
        List<Class> allClasses = this.getAllAspectClass();
        for (Class clazz : allClasses) {
            try {
                Advisor advisor = this.getAdvisorByClass(clazz);
                cache.put(clazz.getName(), advisor);
                advisorList.add(advisor);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return advisorList;
    }

    private List<Class> getAllAspectClass() {
        Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(IAspect.class);
        List<Class> aspectClazz = new ArrayList<>(beanMap.size());
        for (String className : beanMap.keySet()) {
            Object bean = beanMap.get(className);
            aspectClazz.add(bean.getClass());
        }
        return aspectClazz;
    }

    private Advisor getAdvisorByClass(Class clazz) {
        Method[] methods = clazz.getDeclaredMethods();
        Advisor advisor = new Advisor();
        for (Method method : methods) {
            if (method.isAnnotationPresent(IBefore.class)) {
                advisor.setAspectBeforeMethod(method);
            } else if (method.isAnnotationPresent(IAfter.class)) {
                advisor.setAspectAfterMethod(method);
            } else if (method.isAnnotationPresent(IPointcut.class)) {
                advisor.setPointCut(method.getAnnotation(IPointcut.class).value());
            }
        }
        Object bean = applicationContext.getBean(clazz);
        advisor.setAspectObj(bean);
        advisor.setAspectClass(clazz.getName());
        return advisor;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.parse();
    }


}
